///////////////////////////////////////////////////////////////////////
// 模块描述：这是一个简单的cache控制模块
// 组织方式：直接映射
// 容量：16KB
// 块大小：4个字、16字节、128位
// 块数量：16K / 16 =1024
// 写策略：写返回, 在写缺失时更新内存
// 地址位宽：32 bit
// CPU 字(32bit)对齐访问：
//////////////////////////////////////////////////////////////////////




module cache_ctr (
    input clk,
    input rst,

    //CPU->cache
    input [31:0] cpu_req_addr,
    input [31:0] cpu_req_data,
    input cpu_req_rw,
    input cpu_req_valid,
    //memory->cache
    input [127:0] mem_rsp_data,
    input mem_rsp_ready,
    //cache->memory
    output reg [31:0] cache_req_addr,
    output reg [127:0] cache_req_data,
    output reg cache_req_rw,
    output reg cache_req_valid,
    //cache->CPU
    output reg [31:0] cache_rsp_data,
    output reg cache_ready
);

localparam IDLE=3'd0;
localparam COMPARE_TAG=3'd1;
localparam ALLOCATE=3'd2;
localparam WRITE_BACK=3'd3;


reg [2:0] current_state, next_state;
reg [31:0] cache_req_addr_nxt;
reg [127:0] cache_req_data_nxt;
reg cache_req_rw_nxt;
reg cache_req_valid_nxt;
reg [31:0] cache_rsp_data_nxt;
reg cache_ready_nxt;
reg [31:0] data_read; //cache->cpu
reg [127:0] data_write;//cache->memory
reg write_cache_line_vbit;
reg write_cache_line_dbit;
reg [17:0] write_cache_line_tagf;
reg [127:0] write_cache_line_data;

reg cache_we;
wire [9:0] idex;
wire cache_line_valid;
wire cache_line_dirty;
wire [17:0] cache_line_tag;
wire [127:0] cache_line_data_rd;
wire cache_hit;
wire cache_clean;

assign idex = cpu_req_addr[13:4];
assign cache_hit = (cpu_req_addr[31:14]==cache_line_tag) & cache_line_valid;
assign cache_clean = (~cache_line_dirty)|(~cache_line_valid);

always @(*) begin
    data_write = cache_line_data_rd;
    case(cpu_req_addr[3:2])
        2'b00 : begin
            data_read = cache_line_data_rd[31:0];
            data_write[31:0] =  cpu_req_data;
        end
        2'b01 : begin
            data_read = cache_line_data_rd[63:32];
            data_write[63:32] =  cpu_req_data;
        end
        2'b10 : begin
            data_read = cache_line_data_rd[95:64];
            data_write[95:64] =  cpu_req_data;
        end
        2'b11 : begin
            data_read = cache_line_data_rd[127:96];
            data_write[127:96] =  cpu_req_data;
        end
    endcase
end

//FSM
always @(posedge clk ) begin
    if(rst) begin
        current_state <= IDLE;
        cache_req_addr <= 'd0;
        cache_req_data <= 'd0;
        cache_req_rw <= 'b0;
        cache_req_valid <= 'b0;
        cache_rsp_data <= 'd0;
        cache_ready <= 'd0;
    end
    else begin
        current_state <= next_state;
        cache_req_addr <= cache_req_addr_nxt;
        cache_req_data <= cache_req_data_nxt;
        cache_req_rw <= cache_req_rw_nxt;
        cache_req_valid <= cache_req_valid_nxt;
        cache_rsp_data <= cache_rsp_data_nxt;
        cache_ready <= cache_ready_nxt;
    end
end
always @(*) begin
    next_state = current_state;
    cache_req_addr_nxt = cpu_req_addr;
    cache_req_data_nxt = cache_line_data_rd;
    cache_req_rw_nxt = 1'b0;
    cache_req_valid_nxt = 1'b0;
    cache_rsp_data_nxt = data_read;
    cache_ready_nxt = 1'b0;
    cache_we = 1'b0;
    write_cache_line_vbit = cache_line_valid;
    write_cache_line_dbit = cache_line_dirty;
    write_cache_line_tagf = cpu_req_addr[31:14];
    write_cache_line_data = data_write;
    case (current_state)
        IDLE : begin
            if(cpu_req_valid)
                next_state = COMPARE_TAG;
        end
        COMPARE_TAG : begin
            if(cache_hit)  begin//cache hit
                next_state = IDLE;
                cache_ready_nxt = 1'b1;
                if(cpu_req_rw) begin//cpu write data
                    cache_we = 1'b1;
                    write_cache_line_vbit = 1'b1;
                    write_cache_line_dbit = 1'b1;
                    write_cache_line_tagf = cache_line_tag;
                end
            end
            else if(cache_clean) begin//cache miss and cache line is clean
                next_state = ALLOCATE;
                // cache_req_addr_nxt = cpu_req_addr;
                // cache_req_rw_nxt = 1'b0;
                cache_req_valid_nxt = 1'b1;

            end
            else begin //cache miss and cache line is dirty
                next_state = WRITE_BACK;
                cache_req_addr_nxt = {cache_line_tag,cpu_req_addr[13:0]};
                // cache_req_data_nxt = cache_line_data_rd;
                cache_req_rw_nxt = 1'b1;
                cache_req_valid_nxt = 1'b1;
            end
        end
        ALLOCATE : begin
            if(mem_rsp_ready) begin
                next_state = COMPARE_TAG;
                cache_we = 1'b1;
                write_cache_line_vbit = 1'b1;
                write_cache_line_dbit = 1'b0;
                write_cache_line_data = mem_rsp_data;
            end
            else begin
                cache_req_addr_nxt = cache_req_addr;
                cache_req_rw_nxt = cache_req_rw;
                cache_req_valid_nxt = cache_req_valid;
            end
        end
        WRITE_BACK : begin
            if(mem_rsp_ready) begin
                next_state = ALLOCATE;
                cache_req_addr_nxt = cpu_req_addr;
                cache_req_rw_nxt = 1'b0;
                cache_req_valid_nxt = 1'b1;
            end
            else begin
                cache_req_addr_nxt = cache_req_addr;
                cache_req_data_nxt = cache_req_data;
                cache_req_rw_nxt = cache_req_rw;
                cache_req_valid_nxt = cache_req_valid;
            end
        end
    endcase
end


//connect tag and data ram
cache_tag_ram ctag(
    .clk(clk),
    .rst(rst),
    .idex(idex),
    .we(cache_we),
    .write_cache_line_vbit(write_cache_line_vbit),//cache line valid bit
    .write_cache_line_dbit(write_cache_line_dbit),//cache line dirty bit
    .write_cache_line_tagf(write_cache_line_tagf),//cache line tag field
    .cache_line_vbit(cache_line_valid),
    .cache_line_dbit(cache_line_dirty),
    .cache_line_tagf(cache_line_tag)
);

cache_data_ram cdata(
    .clk(clk),
    .rst(rst),
    .idex(idex),
    .we(cache_we),
    .write_cache_line_data(write_cache_line_data),
    .cache_line_data(cache_line_data_rd)
);

endmodule //cache_ctr